﻿using Abp.Authorization;
using Abp.Dependency;
using Abp.Domain.Repositories;
using Abp.Domain.Uow;
using Abp.EntityFrameworkCore.EFPlus;
using Abp.Json;
using Abp.Notifications;
using Abp.ObjectMapping;
using Abp.Runtime.Session;
using Abp.Timing;
using Castle.Core.Logging;
using Microsoft.EntityFrameworkCore;
using Mt.Site.Core.DiyContent.Form.Entities;
using Mt.Site.Core.Notifications;
using Mt.Site.Core.Storage;
using Mt.Site.Core.VersionBackup.Entities;
using Mt.Site.Core.WorkFlows.Design;
using Mt.Site.Core.WorkFlows.Diy;
using Mt.Site.Core.WorkFlows.Enum;
using Mt.Site.Core.WorkFlows.Permissions;
using Mt.Site.Core.WorkFlows.Process.Entities;
using Mt.Site.Core.WorkFlows.Process.Models;
using Mt.Site.Core.WorkFlows.Process.Stores;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Mt.Site.Core.WorkFlows.Process.Impl
{
    public class WorkFlowSuperManager  : IWorkFlowSuperManager, ITransientDependency
    {
        private readonly IPermissionChecker _permissionChecker;

        private readonly IRepository<WorkFlowPaper, Guid> _workFlowPaperRepository;
        private readonly IRepository<WorkFlowPaperData, Guid> _workFlowPaperDataRepository;
        private readonly IRepository<WorkFlowPaperListCtrlData, long> _workFlowPaperListCtrlDataRepository;

        private readonly IWorkFlowProcessStore _workFlowProcessStore;
        private readonly IRepository<WorkFlowLogs, long> _workFlowLogsRepository;

        private readonly IRepository<WorkFlowRelationship, long> _workFlowRelationshipRepository;
        private readonly IRepository<WorkFlowProcessMsg, long> _workFlowProcessMsgRepository;

        private readonly IRepository<TenantNotificationInfo, Guid> _tenantNotificationRepository;
        private readonly IRepository<MtUserNotificationInfo, Guid> _userNotificationRepository;

        private readonly IRepository<WorkFlowPaperArchive, Guid> _workFlowPaperArchiveRepository;

        private readonly IRepository<WorkFlowRunTime, long> _workFlowRuntimeRepository;

        private readonly IRepository<FileObject, Guid> _fileObjectRepository;
        private readonly IRepository<BackupBody, long> _backupBodyRepository;

        private readonly IWorkFlowFormDataStore _workFlowFormDataStore;
        private readonly IWorkFlowFactory _workFlowFactory;

        public IObjectMapper ObjectMapper { get; set; }
        public ILogger Loger { get; set; }
        public IAbpSession AbpSession { get; set; }

        private readonly IIocResolver _iocResolver;

        private readonly IWorkFlowPluginManager _workFlowPluginManager;

        private readonly IWorkFlowRollBackService _workFlowRollBackService;


        private readonly IUnitOfWorkManager _unitOfWorkManager;

        public WorkFlowSuperManager(
            IPermissionChecker permissionChecker,
            IRepository<FileObject, Guid> fileObjectRepository,
            IRepository<BackupBody, long> backupBodyRepository,
            IRepository<WorkFlowPaper, Guid> workFlowPaperRepository,
            IRepository<WorkFlowPaperData, Guid> workFlowPaperDataRepository,
            IRepository<WorkFlowPaperListCtrlData, long> workFlowPaperListCtrlDataRepository,
            IWorkFlowProcessStore workFlowProcessStore,
            IRepository<WorkFlowLogs, long> workFlowLogsRepository,
            IRepository<WorkFlowRelationship, long> workFlowRelationshipRepository,
            IRepository<WorkFlowProcessMsg, long> workFlowProcessMsgRepository,
            IRepository<WorkFlowPaperArchive, Guid> workFlowPaperArchiveRepository,
            IRepository<WorkFlowRunTime, long> workFlowRuntimeRepository,
            IWorkFlowFormDataStore workFlowFormDataStore,
            IWorkFlowFactory workFlowFactory,
            IWorkFlowPluginManager workFlowPluginManager,
            IIocResolver iocResolver,
            IUnitOfWorkManager unitOfWorkManager,
            IRepository<TenantNotificationInfo, Guid> tenantNotificationRepository,
            IRepository<MtUserNotificationInfo, Guid> userNotificationRepository,
            IWorkFlowRollBackService workFlowRollBackService)
        {
            _permissionChecker = permissionChecker;

            _fileObjectRepository = fileObjectRepository;
            _backupBodyRepository = backupBodyRepository;
            _workFlowPaperRepository = workFlowPaperRepository;
            _workFlowPaperDataRepository = workFlowPaperDataRepository;
            _workFlowPaperListCtrlDataRepository = workFlowPaperListCtrlDataRepository;

            _workFlowProcessStore = workFlowProcessStore;
            _workFlowLogsRepository = workFlowLogsRepository;

            _workFlowRelationshipRepository = workFlowRelationshipRepository;
            _workFlowProcessMsgRepository = workFlowProcessMsgRepository;

            _tenantNotificationRepository = tenantNotificationRepository;
            _userNotificationRepository = userNotificationRepository;
            _workFlowPaperArchiveRepository = workFlowPaperArchiveRepository;
            _workFlowRuntimeRepository = workFlowRuntimeRepository;

            _workFlowFormDataStore = workFlowFormDataStore;
            _workFlowFactory = workFlowFactory;
            _workFlowPluginManager = workFlowPluginManager;

            _iocResolver = iocResolver;
            _workFlowRollBackService = workFlowRollBackService;

            _unitOfWorkManager = unitOfWorkManager;

            AbpSession = NullAbpSession.Instance;
            ObjectMapper = NullObjectMapper.Instance;
            Loger = NullLogger.Instance;
        }

        #region 删除

        //草稿箱删除
        public async Task DeleteAsync(List<Guid> idList)
        {
            var userId = AbpSession.UserId;

            foreach (var paperId in idList)
            {
                var paper = await _workFlowPaperRepository.FirstOrDefaultAsync(c=> c.Id == paperId);
                if (paper != null)
                {
                    //稿件必须处于拟稿状态，且操作者是拟稿人
                    if ((paper.PaperState == PaperState.Draft || paper.PaperState == PaperState.Temporary) && paper.CreatorUserId == userId)
                    {
                        await DeleteAsync(paper);
                    }
                }
            }
        }

        //管理员删除公文（稿件先要作废才能删）
        public async Task SuperDeleteAsync(List<Guid> idList)
        {
            if(await _permissionChecker.IsGrantedAsync(WorkFlowPermissions.Pages_Manage_WorkFlow_Admin))
            {
                foreach (var paperId in idList)
                {
                    var paper = await _workFlowPaperRepository.FirstOrDefaultAsync(c => c.Id == paperId);
                    if (paper != null && (paper.PaperState == PaperState.Cancelled || paper.PaperState == PaperState.Temporary))
                    {
                        await DeleteAsync(paper);
                    }
                }
            }
        }

        public async Task DeleteArchiveAsync(Guid paperId)
        {
            if (await _permissionChecker.IsGrantedAsync(WorkFlowPermissions.Pages_Manage_WorkFlow_Admin))
            {
                var paper = await _workFlowPaperArchiveRepository.FirstOrDefaultAsync(c => c.Id == paperId);
                if (paper != null)
                {
                    await DeleteInternalAsync(paperId);

                    await _workFlowPaperArchiveRepository.DeleteAsync(paperId);
                }
            }
        }

        private async Task DeleteAsync(WorkFlowPaper paperd)
        {
            var workFlowDefinition = await GetWorkFlowDefinitionAsync(paperd.WorkFlowId);

            using (var diyfunction = _workFlowPluginManager.GetWorkFlowDiyFunction(workFlowDefinition, _iocResolver))
            {
                await diyfunction.Object.BeforeDeletedAsync(this);

                await DeleteInternalAsync(paperd.Id);

                await diyfunction.Object.AfterDeletedAsync(this);
            }
        }

        private async Task DeleteInternalAsync(Guid paperId)
        {
            var paper = await _workFlowPaperRepository.FirstOrDefaultAsync(paperId);
            if (paper != null)
            {
                var workFlowDefinition = await GetWorkFlowDefinitionAsync(paper.WorkFlowId);
                using (var diyfunction = _workFlowPluginManager.GetWorkFlowDiyFunction(workFlowDefinition, _iocResolver))
                {
                    await diyfunction.Object.CleanRuntimeDataAsync(paperId, _workFlowRuntimeRepository);
                }
            }

            // 如果稿件已办结则不删附件
            if (paper != null && paper.PaperState != PaperState.Finished)
            {
                //删除附件
                var filelist = await _fileObjectRepository.GetAllListAsync(c => c.ContentId == paperId);
                foreach (var item in filelist)
                {
                    await _fileObjectRepository.HardDeleteAsync(item);
                }
            }

            await _backupBodyRepository.BatchDeleteAsync(t => t.ContentId == paperId);

            //删除轨迹、日志
            await _workFlowProcessStore.RemoveAsync(c => c.PaperId == paperId);
            await _workFlowLogsRepository.BatchDeleteAsync(c => c.PaperId == paperId);
            //删除稿件
            var dataList = await _workFlowPaperDataRepository.GetAllListAsync(c => c.PaperId == paperId);
            if (dataList.Count > 0)
            {
                var dataIdList = dataList.Select(c => c.Id).ToList();
                await _workFlowPaperListCtrlDataRepository.BatchDeleteAsync(c => dataIdList.Contains(c.RelateId));
                await _workFlowPaperDataRepository.DeleteAsync(c => c.PaperId == paperId);
            }

            await _workFlowRelationshipRepository.DeleteAsync(c => c.PaperId == paperId);
            await _workFlowProcessMsgRepository.DeleteAsync(c => c.PaperId == paperId);

            //删除通知消息
            var idStr = paperId.ToJsonString();
            var query = from userNotificationInfo in _userNotificationRepository.GetAll()
                        join tenantNotificationInfo in _tenantNotificationRepository.GetAll() on userNotificationInfo.TenantNotificationId equals tenantNotificationInfo.Id
                        where tenantNotificationInfo.EntityId == idStr
                        select userNotificationInfo;
            var list = await query.ToListAsync();
            foreach (var item in list)
            {
                await _userNotificationRepository.DeleteAsync(item);
            }
            await _tenantNotificationRepository.DeleteAsync(c => c.EntityId == idStr);

            await _workFlowPaperRepository.DeleteAsync(paperId);
        }

        #endregion


        #region 作废+恢复

        //作废
        public async Task CancelAsync(List<Guid> idList)
        {
            foreach (var paperId in idList)
            {
                var paper = await _workFlowPaperRepository.FirstOrDefaultAsync(c => c.Id == paperId);
                if (paper != null && paper.PaperState == PaperState.Running)
                {
                    var workFlowDefinition = await GetWorkFlowDefinitionAsync(paper.WorkFlowId);

                    using (var diyfunction = _workFlowPluginManager.GetWorkFlowDiyFunction(workFlowDefinition, _iocResolver))
                    {
                        await diyfunction.Object.BeforeCanceledAsync(this);

                        paper.ChangePaperState(PaperState.Cancelled);

                        await diyfunction.Object.CleanRuntimeDataAsync(paperId, _workFlowRuntimeRepository);

                        await diyfunction.Object.AfterCanceledAsync(this);
                    }
                }
            }
        }

        //恢复
        public async Task RestoreAsync(List<Guid> idList)
        {
            var now = Clock.Now;

            foreach (var paperId in idList)
            {
                var paper = await _workFlowPaperRepository.FirstOrDefaultAsync(c => c.Id == paperId);
                if (paper != null && paper.PaperState == PaperState.Cancelled)
                {
                    var workFlowDefinition = await GetWorkFlowDefinitionAsync(paper.WorkFlowId);

                    using (var diyfunction = _workFlowPluginManager.GetWorkFlowDiyFunction(workFlowDefinition, _iocResolver))
                    {
                        await diyfunction.Object.BeforeRestoreAsync(this);

                        paper.ChangePaperState(PaperState.Running);

                        //找到所有处于待办状态的任务，重置时间
                        var taskList = await _workFlowProcessStore.WorkFlowProcessRepository.GetAllListAsync(c => c.PaperId == paper.Id && c.State == ActionState.Todo);
                        foreach (var task in taskList)
                        {
                            task.StartTime = now;
                            task.HasMarked = false;
                            task.MarkedTime = null;
                        }

                        await diyfunction.Object.AfterRestoreAsync(this);
                    }
                }
            }

            await _unitOfWorkManager.Current.SaveChangesAsync();

            foreach (var paperId in idList)
            {
                var paper = await _workFlowPaperRepository.FirstOrDefaultAsync(c => c.Id == paperId);
                if (paper != null)
                {
                    var workFlowDefinition = await GetWorkFlowDefinitionAsync(paper.WorkFlowId);
                    var allData = await _workFlowProcessStore.GetAllProcessDataAsync(paperId);
                    using (var diyfunction = _workFlowPluginManager.GetWorkFlowDiyFunction(workFlowDefinition, _iocResolver))
                    {
                        await diyfunction.Object.ResetRuntimeDataAsync(paperId, _workFlowRuntimeRepository, allData);
                    }
                }
            }
        }

        #endregion


        #region 已办结的稿件，恢复为审核状态

        //已办结的稿件，恢复为审核状态
        public async Task ResumeExecuteAsync(Guid paperId)
        {
            var paper = await _workFlowPaperRepository.FirstOrDefaultAsync(c => c.Id == paperId);
            IWorkflowDefinition workFlowDefinition = null;
            if (paper != null && paper.PaperState == PaperState.Finished)
            {
                workFlowDefinition = await GetWorkFlowDefinitionAsync(paper.WorkFlowId);
                using (var diyfunction = _workFlowPluginManager.GetWorkFlowDiyFunction(workFlowDefinition, _iocResolver))
                {
                    await diyfunction.Object.BeforeResumeExecuteAsync(this, paperId);

                    // 1、稿件状态: 办结->running
                    paper.ChangePaperState(PaperState.Running);

                    // 2、恢复最后一个操作者的待办状态 ，并重置时间
                    var task = await _workFlowProcessStore.WorkFlowProcessRepository.Query(
                        q => q.Where(c => c.PaperId == paper.Id).OrderByDescending(c => c.FinishedTime).FirstOrDefaultAsync());

                    task.StartTime = Clock.Now;
                    task.MarkedTime = null;
                    task.HasMarked = false;
                    task.State = ActionState.Todo;
                    task.FinishedTime = null;
                    if (task.IsDefaultMsg)
                    {
                        task.Reply = null;
                        task.ReplyTime = null;
                    }

                    //删日志
                    await _workFlowLogsRepository.DeleteAsync(c => c.ProcessId == task.Id);

                    await diyfunction.Object.AfterResumeExecuteAsync(this);
                }
            }

            await _unitOfWorkManager.Current.SaveChangesAsync();

            if (workFlowDefinition != null)
            {
                var allData = await _workFlowProcessStore.GetAllProcessDataAsync(paperId);
                using (var diyfunction = _workFlowPluginManager.GetWorkFlowDiyFunction(workFlowDefinition, _iocResolver))
                {
                    await diyfunction.Object.ResetRuntimeDataAsync(paperId, _workFlowRuntimeRepository, allData);
                }
            }
        }
        #endregion


        #region  表单数据编辑（管理员）

        public async Task<GetFormDataOutput> GetFormDataForEditAsync(Guid paperId)
        {
            var paper = await _workFlowPaperRepository.GetAsync(paperId);
            var workflow = await GetWorkFlowDefinitionAsync(paper.WorkFlowId);

            return await _workFlowFormDataStore.GetFormDataForSuperEditAsync(paperId, workflow, paper.NodeId, paper.CreatorUserId.Value);
        }

        public async Task UpdateFormDataAsync(UpdateFormDataInput input)
        {
            var paperId = input.Id;
            var paper = await _workFlowPaperRepository.GetAsync(paperId);

            var workFlowDefinition = await GetWorkFlowDefinitionAsync(paper.WorkFlowId);

            using (var diyfunction = _workFlowPluginManager.GetWorkFlowDiyFunction(workFlowDefinition, _iocResolver))
            {
                await diyfunction.Object.BeforeSuperSaveFormAsync(input, paper, this);

                paper.ChangeTitle(input.Paper.Title);
                await _workFlowFormDataStore.SuperUpdateAsync(input.Values, paperId, workFlowDefinition, paper.NodeId);

                await diyfunction.Object.AfterSuperSaveFormAsync(input, paper, this);
            }
        }
        #endregion


        /// <summary>
        /// 回退到任意操作人
        /// </summary>
        /// <param name="taskId"></param>
        /// <returns></returns>
        public async Task RollBack(Guid taskId, Guid paperId)
        {
            var paper = await _workFlowPaperRepository.GetAsync(paperId);
            IWorkflowDefinition workFlowDefinition = null;
            if (paper != null)
            {
                workFlowDefinition = await GetWorkFlowDefinitionAsync(paper.WorkFlowId);

                using (var diyfunction = _workFlowPluginManager.GetWorkFlowDiyFunction(workFlowDefinition, _iocResolver))
                {
                    await _workFlowRollBackService.RollBackAsync(taskId, paperId, diyfunction.Object.WorkFlowRollBackDiyService);
                }
            }

            await _unitOfWorkManager.Current.SaveChangesAsync();

            if (workFlowDefinition != null)
            {
                var allData = await _workFlowProcessStore.GetAllProcessDataAsync(paperId);
                using (var diyfunction = _workFlowPluginManager.GetWorkFlowDiyFunction(workFlowDefinition, _iocResolver))
                {
                    await diyfunction.Object.ResetRuntimeDataAsync(paperId, _workFlowRuntimeRepository, allData);
                }
            }
        }

        public async Task UpdateReplyMsgAsync(Guid taskId, string replyMsg)
        {
            var task = await _workFlowProcessStore.WorkFlowProcessRepository.FirstOrDefaultAsync(c => c.Id == taskId);
            if (task != null)
            {
                task.Reply = replyMsg;
            }
        }

        public async Task UpdateAdditionalMsgAsync(long msgId, string replyMsg)
        {
            var task = await _workFlowProcessMsgRepository.FirstOrDefaultAsync(c => c.Id == msgId);
            if (task != null)
            {
                task.Reply = replyMsg;
            }
        }


        public async Task RemoveTemporaryPaperAsync()
        {
            var dateTime = DateTime.Now.AddDays(-1);
            var query = from c in _workFlowPaperRepository.GetAll()
                        where c.PaperState == PaperState.Temporary && c.CreationTime < dateTime
                        select c.Id;

            var idList = query.ToList();
            foreach (var id in idList)
            {
                await DeleteInternalAsync(id);
            }
        }


        public async Task SetPaperUnMarked(Guid id)
        {
            var process = await _workFlowProcessStore.WorkFlowProcessRepository.FirstOrDefaultAsync(item => item.Id == id && item.State == ActionState.Todo);

            if (process != null)
            {
                process.HasMarked = false;
                process.MarkedTime = null;
            }
        }

        private async Task<IWorkflowDefinition> GetWorkFlowDefinitionAsync(Guid workFlowId)
        {
            return await _workFlowFactory.GetWorkflowDefinitionAsync(workFlowId);
        }

        public async Task UpdateAduditTimeAsync(DateTime startTime,DateTime markTime,DateTime finishTime,Guid id)
        {
            var processEnity = await _workFlowProcessStore.WorkFlowProcessRepository.GetAsync(id);
            processEnity.StartTime = startTime;
            processEnity.MarkedTime = markTime;
            processEnity.FinishedTime = finishTime;
            processEnity.ReplyTime = finishTime;

            var logEntity = await _workFlowLogsRepository.FirstOrDefaultAsync(item => item.ProcessId == processEnity.Id && item.PaperId == processEnity.PaperId);
            if (logEntity != null)
            {
                logEntity.CreationTime = finishTime;
            }


            await _unitOfWorkManager.Current.SaveChangesAsync();

            var paper = await _workFlowPaperRepository.FirstOrDefaultAsync(c => c.Id == processEnity.PaperId);
            if (paper != null)
            {
                var workFlowDefinition = await GetWorkFlowDefinitionAsync(paper.WorkFlowId);
                var allData = await _workFlowProcessStore.GetAllProcessDataAsync(processEnity.PaperId);
                using (var diyfunction = _workFlowPluginManager.GetWorkFlowDiyFunction(workFlowDefinition, _iocResolver))
                {
                    await diyfunction.Object.ResetRuntimeDataAsync(processEnity.PaperId, _workFlowRuntimeRepository, allData);
                }
            }
        }

        public async Task ResetRuntimeDataAsync(Guid paperId)
        {
            var paper = await _workFlowPaperRepository.FirstOrDefaultAsync(c => c.Id == paperId);
            var workFlowDefinition = await GetWorkFlowDefinitionAsync(paper.WorkFlowId);

            using (var diyfunction = _workFlowPluginManager.GetWorkFlowDiyFunction(workFlowDefinition, _iocResolver))
            {
                if (paper == null || paper.PaperState == PaperState.Cancelled)
                {
                    await diyfunction.Object.CleanRuntimeDataAsync(paperId, _workFlowRuntimeRepository);
                }

                var newAllData = await _workFlowProcessStore.GetAllProcessDataAsync(paperId);
                await diyfunction.Object.ResetRuntimeDataAsync(paperId, _workFlowRuntimeRepository, newAllData);
            }
        }

        public async Task ArchiveAsync(Guid paperId)
        {
            var paper = await _workFlowPaperRepository.FirstOrDefaultAsync(c => c.Id == paperId);
            IWorkflowDefinition workFlowDefinition = null;
            if (paper != null && paper.PaperState == PaperState.Finished)
            {
                workFlowDefinition = await GetWorkFlowDefinitionAsync(paper.WorkFlowId);
                using (var diyfunction = _workFlowPluginManager.GetWorkFlowDiyFunction(workFlowDefinition, _iocResolver))
                {
                    await diyfunction.Object.BeforeArchivedAsync(paper);

                    var newEntity = ObjectMapper.Map<WorkFlowPaperArchive>(paper);
                    newEntity.ArchivedOperator = "";
                    newEntity.ArchivedTime = Clock.Now;

                    await _workFlowPaperArchiveRepository.InsertAsync(newEntity);
                    await _workFlowPaperRepository.DeleteAsync(paperId);

                    await diyfunction.Object.AfterArchivedAsync(paperId);
                }
            }

            await _unitOfWorkManager.Current.SaveChangesAsync();

            if (workFlowDefinition != null)
            {
                using (var diyfunction = _workFlowPluginManager.GetWorkFlowDiyFunction(workFlowDefinition, _iocResolver))
                {
                    await diyfunction.Object.CleanRuntimeDataAsync(paperId, _workFlowRuntimeRepository);
                }
            }
        }
    }
}
